#include <iostream>
#include <string>
#include <stdio.h>
#include <fstream>
#include <sstream>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <libxml++/libxml++.h>
#include "../../http-loader.hh"
#include <pthread.h>
#include <unistd.h>
#include <gtkmm.h>

#define FILENAME "tmp.xml"
//#define FILENAME "img-retriver.xml"

using namespace std;

void get_all_node(vector<xmlpp::Node*>& all_nodes, const xmlpp::Node& root){
	xmlpp::Node::NodeList list = root.get_children();
	for(xmlpp::Node::NodeList::const_iterator it = list.begin(); it != list.end(); it++)
	{
		all_nodes.push_back(*it);
		get_all_node(all_nodes, **it);
	}
}

vector<xmlpp::Node*> get_image_list(const xmlpp::Node& root, const string image_tag){
	vector<xmlpp::Node*> ret;
	vector<xmlpp::Node*> all_nodes;
	get_all_node(all_nodes, root);
	for(vector<xmlpp::Node*>::const_iterator it = all_nodes.begin(); it != all_nodes.end(); it++)
		if ((*it)->get_name() == image_tag)
			ret.push_back(*it);
	return ret;
}

#define URL_TAG "url"
#define LOCAL_TAG "local"

bool is_fixable(xmlpp::Node& node){
	xmlpp::Node::NodeList list = node.get_children();
	for(xmlpp::Node::NodeList::const_iterator it = list.begin(); it != list.end(); it++)
		if ((*it)->get_name() == LOCAL_TAG)
			return false;
	for(xmlpp::Node::NodeList::const_iterator it = list.begin(); it != list.end(); it++)
		if ((*it)->get_name() == URL_TAG)
			return true;
	return false;
}

vector<string> get_urls(xmlpp::Node& node){
	vector<string> ret;
	xmlpp::Node::NodeList list = node.get_children();
	for(xmlpp::Node::NodeList::const_iterator it = list.begin(); it != list.end(); it++)
		if ((*it)->get_name() == URL_TAG)
		{
			xmlpp::TextNode* text_node = dynamic_cast<xmlpp::TextNode*>(*it);
			if (text_node)
				ret.push_back(text_node->get_content());
		}
	return ret;
}

void delete_file(const string file){
	cout << "Delete file " << file;
	if (remove(file.c_str()))
		perror("remove");
	else
		cout << "ok" << endl;
}

class get_unique_file_name{
	private:
		static int cnt;
	public:
		static string get(){
			while (true){
				stringstream ss;
				ss << cnt++;
				ifstream ifs(ss.str().c_str());
				if (ifs.good())
					ifs.close();
				else {
					ofstream ofs(ss.str().c_str());
					if (ofs.good()){
						ofs.close();
						return ss.str();
					} else ofs.close();
				}
			}
			return "";
		}
};

int get_unique_file_name::cnt = 0;

class download_daemon{
	private:
		pthread_t thread;
		bool is_running_;
	public:
		download_daemon():is_running_(false){ };
		bool is_running(){return is_running_;}
		void download(const string file, const string url, xmlpp::Element& node){
			pthread_create(&thread, NULL, download_daemon::down_true_daemon, new tuple<string, string, bool*, xmlpp::Element*>(url, file, &is_running_, &node));
		}
		static void* down_true_daemon(void *data){
			tuple<string, string, bool*, xmlpp::Element*>* args(static_cast<tuple<string, string, bool*, xmlpp::Element*>*>(data));
			if (args == NULL) return NULL;
			string url = get<0>(*args);
			string file = get<1>(*args);
			bool& is_run(*get<2>(*args));
			xmlpp::Element& node(*get<3>(*args));
			if (HttpLoader::down_ok(url, file))
				node.add_child_text(Glib::ustring(LOCAL_TAG))->set_content(file.c_str());
			else delete_file(file);
			is_run = false;
			delete args;
			return NULL;
		}
};

#define DEFAULT_NTHREADS 4
class download_manager{
	private:
		static download_manager* instance_;
		static int default_nthreads_;
		int nthreads_;
		int max_nthreads_;
		download_manager() : nthreads_(default_nthreads_), max_nthreads_(nthreads_){}
		map<int,download_daemon*> daemons;
	public:
		static download_manager& get_instance(){ if (instance_ == NULL) instance_ = new download_manager();
			return *instance_;
		}
		void set_nthreads(int nthreads){nthreads_ = nthreads; max_nthreads_ = max(max_nthreads_, nthreads_);}
		bool download(const string file, const string url, xmlpp::Element& node){
			bool found = false;
			do{
				for(int i = 0; i < nthreads_; i++){
					if (daemons[i] == NULL)
						daemons[i] = new download_daemon();
					if (!daemons[i]->is_running()){
						daemons[i]->download(file, url, node);
						found = true;
					}
				}
			} while (!found);
			return true;
		}

		void clean(){
			for(int i = 0; i < max_nthreads_; i++)
				if (daemons[i])
					delete daemons[i];
		}
};

download_manager* download_manager::instance_ = NULL;
int download_manager::default_nthreads_ = DEFAULT_NTHREADS;

void fix(xmlpp::Node& tnode){
	xmlpp::Element* ptr = dynamic_cast<xmlpp::Element*>(&tnode);
	if (!ptr)
		return;
	xmlpp::Element& node(*ptr);
	vector<string> url_list(get_urls(node));
	for(vector<string>::const_iterator it = url_list.begin(); it != url_list.end(); it++)
	{
		string file_name(get_unique_file_name::get());
		download_manager::get_instance().download(file_name, *it, node);
	}
}

#define IMG_TAG "hasImage"
#define OUTPUT_FILE "output.xml"
#define NTHREADS 10
int main(int argc, char** argv){
	xmlpp::DomParser parser;
	parser.set_substitute_entities();
	parser.parse_file(FILENAME);
	download_manager::get_instance().set_nthreads(NTHREADS);

	try{
		vector<xmlpp::Node*> image_list(get_image_list(*parser.get_document()->get_root_node(), "hasImage"));
		for(vector<xmlpp::Node*>::iterator it = image_list.begin(); it != image_list.end(); it++)
			if (is_fixable(**it))
				fix(**it);
	} catch(...){parser.get_document()->write_to_file(OUTPUT_FILE);}
	download_manager::get_instance().clean();
	return 0;
}
